package com.cold.legendary.bprocess.image.creator;

import com.cold.legendary.bprocess.db.enums.PointRunStatusEnum;
import com.cold.legendary.bprocess.image.common.ShapeConstants;
import com.cold.legendary.bprocess.image.manager.BProcessImageManager;
import com.cold.legendary.bprocess.image.model.BArrowInsGraphicInfo;
import com.cold.legendary.bprocess.image.model.BPointGraphicInfo;
import com.cold.legendary.bprocess.image.model.GraphicInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.imageio.ImageIO;
import javax.imageio.stream.ImageOutputStream;
import javax.xml.bind.DatatypeConverter;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.List;

/**
 * @author cold
 * @since 2023/6/12
 */
@Slf4j
@Component
public class BProcessImageCreator {

    private String FONT_NAME = "Arial";
    private static final int FONT_SIZE = 20;
    private static final int DEFAULT_CANVAS_WIDTH = 1500;
    private static final int DEFAULT_CANVAS_HEIGHT = 300;
    protected static final int ARROW_WIDTH = 5;

    protected static Stroke THICK_TASK_BORDER_STROKE = new BasicStroke(3.0f);

    private Graphics2D graphics2D;
    private FontMetrics fontMetrics;

    private BufferedImage processDiagram;


    public String drawImage(String processId, String currentPointId, String currentPointRunStatus) {
        //获取流程图形信息定义
        List<GraphicInfo> graphicInfos = BProcessImageManager.getProcessGraphicInfo(processId);
        //初始化画布
        initialize(graphicInfos);

        boolean afterCurrent = false;
        for (GraphicInfo graphicInfo : graphicInfos) {
            Color color = Color.lightGray;
            if (null != graphicInfo.getShapeId() && graphicInfo.getShapeId().equals(currentPointId)) {
                if (PointRunStatusEnum.PROCESSING.getCode().equals(currentPointRunStatus)) {
                    color = Color.black;
                } else if (PointRunStatusEnum.WAITING.getCode().equals(currentPointRunStatus)) {
                    color = Color.blue;
                } else if (PointRunStatusEnum.FINISH.getCode().equals(currentPointRunStatus)) {
                    color = Color.green;
                } else if (PointRunStatusEnum.ERROR.getCode().equals(currentPointRunStatus)) {
                    color = Color.RED;
                }
                afterCurrent = true;
            } else {
                if (!afterCurrent) {
                    color = Color.black;
                }
            }
            if (graphicInfo instanceof BPointGraphicInfo) {
                BPointGraphicInfo pointGraphicInfo = (BPointGraphicInfo) graphicInfo;
                drawColorRound(color, pointGraphicInfo.getSrcX(), pointGraphicInfo.getSrcY(), pointGraphicInfo.getWidth(), pointGraphicInfo.getHeight());

                drawText(graphicInfo.getShapeName(), color, pointGraphicInfo.getSrcX(), pointGraphicInfo.getSrcY() + pointGraphicInfo.getHeight() + 20);

            } else if (graphicInfo instanceof BArrowInsGraphicInfo) {
                BArrowInsGraphicInfo arrowInsGraphicInfo = (BArrowInsGraphicInfo) graphicInfo;
                drawSequenceFlow(color, arrowInsGraphicInfo.getSrcX(), arrowInsGraphicInfo.getSrcY(),
                        arrowInsGraphicInfo.getTargetX(), arrowInsGraphicInfo.getTargetY());
            }
        }


//        try {
//            ByteArrayOutputStream bs = new ByteArrayOutputStream();
//            ImageOutputStream imOut = ImageIO.createImageOutputStream(bs);
//            ImageIO.write(processDiagram, "JPEG", imOut);
//
//            ImageIO.write(processDiagram, "JPEG", new FileOutputStream("a.jpg"));
//        } catch (Exception e) {
//            log.error("绘制图片异常！", e);
//        }

        String imageData = null;
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ImageOutputStream imOut = ImageIO.createImageOutputStream(baos);
            ImageIO.write(processDiagram, "JPEG", imOut);

            imageData = DatatypeConverter.printBase64Binary(baos.toByteArray());
        } catch (Exception e) {
            log.error("绘制图片异常！", e);
        }

        return imageData;

    }

    /**
     * 初始化流程图的画布
     *
     * @param graphicInfos
     */
    public void initialize(List<GraphicInfo> graphicInfos) {

        int canvasWidth = graphicInfos.stream().map(GraphicInfo::getWidth).reduce((x, y) -> x + y).orElse(0);
        int canvasHeight = 4 * graphicInfos.get(0).getHeight();

        canvasWidth = canvasWidth > DEFAULT_CANVAS_WIDTH ? canvasWidth : DEFAULT_CANVAS_WIDTH;
        canvasHeight = canvasHeight > DEFAULT_CANVAS_HEIGHT ? canvasHeight : DEFAULT_CANVAS_HEIGHT;

        processDiagram = new BufferedImage(canvasWidth, canvasHeight, BufferedImage.TYPE_INT_RGB);

        graphics2D = processDiagram.createGraphics();
        graphics2D.setBackground(new Color(255, 255, 255, 0));
        graphics2D.clearRect(0, 0, canvasWidth, canvasHeight);

        graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        graphics2D.setPaint(Color.black);

        Font font = new Font(FONT_NAME, Font.BOLD, FONT_SIZE);
        graphics2D.setFont(font);
        this.fontMetrics = graphics2D.getFontMetrics();

    }

    public void drawColorRound(Color color, int x, int y, int width, int height) {
        Paint originalPaint = graphics2D.getPaint();
        Stroke originalStroke = graphics2D.getStroke();

        graphics2D.setPaint(color);
        graphics2D.setStroke(THICK_TASK_BORDER_STROKE);

        RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20);
        graphics2D.draw(rect);

        graphics2D.setPaint(originalPaint);
        graphics2D.setStroke(originalStroke);
    }


    public void drawSequenceFlow(Color color, int srcX, int srcY, int targetX, int targetY) {
        Paint originalPaint = graphics2D.getPaint();
        graphics2D.setPaint(color);
        Line2D.Double line = new Line2D.Double(srcX, srcY, targetX, targetY);
        graphics2D.draw(line);
        drawArrowHead(line);
        graphics2D.setPaint(originalPaint);
    }


    public void drawArrowHead(Line2D.Double line) {
        double scaleFactor = 1;
        int doubleArrowWidth = (int) (2 * ARROW_WIDTH / scaleFactor);
        if (doubleArrowWidth == 0) {
            doubleArrowWidth = 2;
        }
        Polygon arrowHead = new Polygon();
        arrowHead.addPoint(0, 0);
        int arrowHeadPoint = (int) (-ARROW_WIDTH / scaleFactor);
        if (arrowHeadPoint == 0) {
            arrowHeadPoint = -1;
        }
        arrowHead.addPoint(arrowHeadPoint, -doubleArrowWidth);
        arrowHeadPoint = (int) (ARROW_WIDTH / scaleFactor);
        if (arrowHeadPoint == 0) {
            arrowHeadPoint = 1;
        }
        arrowHead.addPoint(arrowHeadPoint, -doubleArrowWidth);

        AffineTransform transformation = new AffineTransform();
        transformation.setToIdentity();
        double angle = Math.atan2(line.y2 - line.y1, line.x2 - line.x1);
        transformation.translate(line.x2, line.y2);
        transformation.rotate((angle - Math.PI / 2d));

        AffineTransform originalTransformation = graphics2D.getTransform();
        graphics2D.setTransform(transformation);
        graphics2D.fill(arrowHead);
        graphics2D.setTransform(originalTransformation);
    }


    private void drawText(String text, Color color, int currentX, int currentY) {
        Font font = new Font(FONT_NAME, Font.ITALIC, ShapeConstants.FONT_SIZE);
        FontRenderContext frc = graphics2D.getFontRenderContext();

        Paint originalPaint = graphics2D.getPaint();
        graphics2D.setPaint(color);

        TextLayout textLayout = new TextLayout(text, font, frc);
        textLayout.draw(graphics2D, currentX, currentY);

        graphics2D.setPaint(originalPaint);
    }


    private String fitTextToWidth(String original, int width) {
        String text = original;

        // remove length for "..."
        int maxWidth = width - 10;

        while (fontMetrics.stringWidth(text + "...") > maxWidth && text.length() > 0) {
            text = text.substring(0, text.length() - 1);
        }

        if (!text.equals(original)) {
            text = text + "...";
        }

        return text;
    }

}
